001 /*
002 * Copyright 2006 Stephen McConnell.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package net.dpml.http;
017
018 import java.io.InputStream;
019 import java.net.URI;
020 import java.net.URL;
021 import java.security.SecureRandom;
022 import java.security.KeyStore;
023 import java.security.NoSuchAlgorithmException;
024 import java.security.NoSuchProviderException;
025
026 import javax.net.ssl.KeyManager;
027 import javax.net.ssl.KeyManagerFactory;
028 import javax.net.ssl.TrustManager;
029 import javax.net.ssl.TrustManagerFactory;
030 import javax.net.ssl.SSLContext;
031 import javax.net.ssl.SSLServerSocketFactory;
032
033 import net.dpml.transit.Artifact;
034
035 import org.mortbay.resource.Resource;
036 import org.mortbay.jetty.security.Password;
037
038 /**
039 * SSL socket connector.
040 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
041 * @version 0.0.3
042 */
043 public class SslSocketConnector extends org.mortbay.jetty.security.SslSocketConnector
044 {
045 private static final int HEADER_BUFFER_SIZE = 4*1024;
046 private static final int REQUEST_BUFFER_SIZE = 8*1024;
047 private static final int RESPONSE_BUFFER_SIZE = 32*1024;
048 private static final int MAXIMUM_IDLE_TIME = 30000;
049 private static final int ACCEPT_QUEUE_SIZE = 0;
050 private static final int ACCEPTORS = 1;
051 private static final int SO_LINGER_TIME = 1000;
052 private static final int CONFIDENTIAL_PORT = 0;
053 private static final int INTEGRAL_PORT = 0;
054 private static final boolean ASSUME_SHORT_DISPATCH = false;
055 private static final String KEYSTORE_TYPE = "JKS";
056 private static final String PROTOCOL = "TLS";
057 private static final String ALGORITHM = "SunX509";
058
059 private transient Context m_context;
060 private transient Password m_certificatePassword;
061 private transient Password m_keystorePassword;
062 private transient Password m_trustPassword;
063
064 /**
065 * SSL connector context definition.
066 */
067 public interface Context extends ConnectorContext
068 {
069 /**
070 * Set the cipher suites.
071 * @param suites the default suites argument
072 * @return the cipher suites
073 */
074 //String[] getCipherSuites( String[] suites );
075
076 /**
077 * Return the keystore password.
078 * @param password implementation defined default value
079 * @return the supplied value unless overriden in the deployment configuration
080 */
081 String getKeyStorePassword( String password );
082
083 /**
084 * Return the certificate password.
085 * @param password implementation defined default value
086 * @return the supplied value unless overriden in the deployment configuration
087 */
088 String getCertificatePassword( String password );
089
090 /**
091 * Return the keystore algorithm.
092 * @param algorithm implementation defined default value
093 * @return the supplied value unless overriden in the deployment configuration
094 */
095 String getSecureRandomAlgorithm( String algorithm );
096
097 /**
098 * Return the keystore type.
099 * @param type implementation defined default value
100 * @return the supplied value unless overriden in the deployment configuration
101 */
102 String getKeyStoreType( String type );
103
104 /**
105 * Return the SSL protocol.
106 * @param protocol implementation defined default value
107 * @return the supplied value unless overriden in the deployment configuration
108 */
109 String getProtocol( String protocol );
110
111 /**
112 * Return the keystore location uri.
113 * @param keystore implementation defined default value
114 * @return the supplied value unless overriden in the deployment configuration
115 */
116 URI getKeyStore( URI keystore );
117
118 /**
119 * Return the 'want-client-authentication' policy.
120 * @param flag implementation defined default value
121 * @return the supplied value unless overriden in the deployment configuration
122 */
123 boolean getWantClientAuth( boolean flag );
124
125 /**
126 * Return the 'need-client-authentication' policy.
127 * @param flag implementation defined default value
128 * @return the supplied value unless overriden in the deployment configuration
129 */
130 boolean getNeedClientAuth( boolean flag );
131
132 /**
133 * Return the SSL context provider.
134 * @param provider implementation defined default value
135 * @return the supplied value unless overriden in the deployment configuration
136 */
137 String getProvider( String provider );
138
139 // extras
140
141 /**
142 * Return the keystore algorithm.
143 * @param algorithm implementation defined default value
144 * @return the supplied value unless overriden in the deployment configuration
145 */
146 String getTrustAlgorithm( String algorithm );
147
148 /**
149 * Return the keystore location uri.
150 * @param uri implementation defined default value
151 * @return the supplied value unless overriden in the deployment configuration
152 */
153 URI getTrustStore( URI uri );
154
155 /**
156 * Return the keystore type.
157 * @param type implementation defined default value
158 * @return the supplied value unless overriden in the deployment configuration
159 */
160 String getTrustStoreType( String type );
161
162 /**
163 * Return the trust store password.
164 * @param password implementation defined default value
165 * @return the supplied value unless overriden in the deployment configuration
166 */
167 String getTrustStorePassword( String password );
168 }
169
170 /**
171 * Creation of a new ssl connector.
172 * @param context the deployment context
173 * @exception Exception if an instantiation error occurs
174 */
175 public SslSocketConnector( Context context ) throws Exception
176 {
177 super();
178
179 m_context = context;
180
181 String host = context.getHost( null );
182 if( null != host )
183 {
184 setHost( host );
185 }
186
187 int port = context.getPort();
188 setPort( port );
189
190 int headerBufferSize = context.getHeaderBufferSize( HEADER_BUFFER_SIZE );
191 setHeaderBufferSize( headerBufferSize );
192
193 int requestBufferSize = context.getRequestBufferSize( REQUEST_BUFFER_SIZE );
194 setRequestBufferSize( requestBufferSize );
195
196 int responseBufferSize = context.getResponseBufferSize( RESPONSE_BUFFER_SIZE );
197 setResponseBufferSize( responseBufferSize );
198
199 int maxIdle = context.getMaxIdleTime( MAXIMUM_IDLE_TIME );
200 setMaxIdleTime( maxIdle );
201
202 int queueSize = context.getAcceptQueueSize( ACCEPT_QUEUE_SIZE );
203 setAcceptQueueSize( queueSize );
204
205 int acceptCount = context.getAcceptors( ACCEPTORS );
206 setAcceptors( acceptCount );
207
208 int linger = context.getSoLingerTime( SO_LINGER_TIME );
209 setSoLingerTime( linger );
210
211 int confidentialPort = context.getConfidentialPort( CONFIDENTIAL_PORT );
212 setConfidentialPort( confidentialPort );
213
214 Scheme confidentialScheme = Scheme.parse( context.getConfidentialScheme( "https" ) );
215 setConfidentialScheme( confidentialScheme.getName() );
216
217 int integralPort = context.getIntegralPort( INTEGRAL_PORT );
218 setIntegralPort( integralPort );
219
220 Scheme integralScheme = Scheme.parse( context.getIntegralScheme( "https" ) );
221 setIntegralScheme( integralScheme.getName() );
222
223 // SslSocketConnector$Context
224
225 String certificatePassword = context.getCertificatePassword( null );
226 if( null != certificatePassword )
227 {
228 m_certificatePassword =
229 Password.getPassword( KEYPASSWORD_PROPERTY, certificatePassword, null );
230 setKeyPassword( certificatePassword );
231 }
232
233 String keystorePassword = context.getKeyStorePassword( null );
234 if( null != keystorePassword )
235 {
236 m_keystorePassword = Password.getPassword( PASSWORD_PROPERTY, keystorePassword, null );
237 setPassword( keystorePassword );
238 }
239
240 String algorithm = context.getSecureRandomAlgorithm( ALGORITHM );
241 setSecureRandomAlgorithm( algorithm );
242
243 String protocol = context.getProtocol( PROTOCOL );
244 setProtocol( protocol );
245
246 URI keystore = context.getKeyStore( null );
247 if( null != keystore )
248 {
249 String keystorePath = keystore.toASCIIString();
250 setKeystore( keystorePath );
251 }
252
253 String provider = context.getProvider( null );
254 if( null != provider )
255 {
256 setProvider( provider );
257 }
258
259 String keystoreType = context.getKeyStoreType( KEYSTORE_TYPE );
260 setKeystoreType( keystoreType );
261
262 boolean wantClientAuth = context.getWantClientAuth( false );
263 setWantClientAuth( wantClientAuth );
264
265 boolean needClientAuth = context.getNeedClientAuth( false );
266 setNeedClientAuth( needClientAuth );
267
268 //String[] suites = context.getCipherSuites( (String[]) null );
269 //if( null != suites )
270 //{
271 // setCipherSuites( suites );
272 //}
273 }
274
275 /**
276 * Create a new SSLServerSocketFactory.
277 * @return the factory
278 * @exception Exception if an error occurs during factory creation
279 */
280 protected SSLServerSocketFactory createFactory()
281 throws Exception
282 {
283 final SSLContext context = getSSLContext();
284 KeyManager[] keyManagers = getKeyManagers();
285 TrustManager[] trustManagers = getTrustManagers();
286 SecureRandom random = new SecureRandom();
287 context.init( keyManagers, trustManagers, random );
288 return context.getServerSocketFactory();
289 }
290
291 private KeyManager[] getKeyManagers() throws Exception
292 {
293 final String algorithm = getSecureRandomAlgorithm();
294 final KeyManagerFactory factory = KeyManagerFactory.getInstance( algorithm );
295 final KeyStore store = loadKeyStore();
296 final char[] password = toCharArray( m_certificatePassword );
297 factory.init( store, password );
298 return factory.getKeyManagers();
299 }
300
301 private KeyStore loadKeyStore() throws Exception
302 {
303 final String type = getKeystoreType();
304 final KeyStore keyStore = KeyStore.getInstance( type );
305 final char[] password = toCharArray( m_keystorePassword );
306 final String keyStorePath = getKeystore();
307 final InputStream input = Resource.newResource( keyStorePath ).getInputStream();
308 keyStore.load( input, password );
309 return keyStore;
310 }
311
312 private KeyStore loadTrustStore() throws Exception
313 {
314 final String type = m_context.getTrustStoreType( KEYSTORE_TYPE );
315 final KeyStore store = KeyStore.getInstance( type );
316 final char[] password = toCharArray( m_trustPassword );
317 URI uri = m_context.getTrustStore( null );
318 if( null != uri )
319 {
320 URL url = Artifact.toURL( uri );
321 final InputStream input = Resource.newResource( url ).getInputStream();
322 store.load( input, password );
323 return store;
324 }
325 else
326 {
327 return null;
328 }
329 }
330
331 private TrustManager[] getTrustManagers() throws Exception
332 {
333 final String algorithm = m_context.getTrustAlgorithm( ALGORITHM );
334 final TrustManagerFactory factory = TrustManagerFactory.getInstance( algorithm );
335 final KeyStore store = loadTrustStore();
336 if( store != null )
337 {
338 factory.init( store );
339 return factory.getTrustManagers();
340 }
341 else
342 {
343 return new TrustManager[0];
344 }
345 }
346
347 private char[] toCharArray( Password value )
348 {
349 if( null == value )
350 {
351 return null;
352 }
353 else
354 {
355 return value.toString().toCharArray();
356 }
357 }
358
359 private SSLContext getSSLContext() throws NoSuchAlgorithmException, NoSuchProviderException
360 {
361 final String protocol = getProtocol();
362 final String provider = getProvider();
363 if( null == provider )
364 {
365 return SSLContext.getInstance( protocol );
366 }
367 else
368 {
369 return SSLContext.getInstance( protocol, provider );
370 }
371 }
372 }
373